// BOCS Adaptive - Dynamic Volatility-Based Breakout Channel System
//@version=6

indicator("BOCS Adaptive", "BOCS-ADAPTIVE",  overlay=true, max_boxes_count = 500, max_lines_count = 500)
import TradingView/ta/10

// ============================================================================
// INPUTS
// ============================================================================

overlap = input.bool(false, "Nested Channels", group = "Main Settings", tooltip="When enabled, allows multiple channels to overlap. When disabled, only one channel can exist at a time. Overlapping channels can show multiple breakout levels simultaneously.")
strong = input.bool(true, "Strong Closes Only", "When enabled, breakouts only trigger when more than 50% of the candle body is outside the channel. This reduces false signals from wicks. When disabled, any price movement outside the channel triggers a breakout.", group = "Main Settings")
length_ = input.int(100, title="Normalization Length", minval=1, group = "Main Settings", tooltip="The number of bars used to calculate the highest high and lowest low for price normalization. Higher values create more stable normalization but may be less responsive to recent price changes.")
length = input.int(14, "Box Detection Length", minval=1, group = "Main Settings", tooltip="The number of bars used to detect channel formation patterns. Lower values create more frequent channels but may be more sensitive to noise. Higher values create fewer but potentially more significant channels.")

// ATR-based TP/SL Settings
show_tpsl = input.bool(true, "Show TP/SL Levels", group = "ATR TP/SL Settings")
atr_timeframe = input.timeframe("1", "ATR Timeframe", group = "ATR TP/SL Settings", tooltip="Timeframe for ATR calculation (e.g., 1min, 5min, 15min)")
atr_length = input.int(14, "ATR Length", minval=1, group = "ATR TP/SL Settings", tooltip="Number of periods for ATR calculation")
tp1_multiplier = input.float(2.0, "Take Profit 1 Multiplier", minval=0.1, step=0.1, group = "ATR TP/SL Settings", tooltip="TP1 distance = ATR × this multiplier")
sl_multiplier = input.float(1.0, "Stop Loss Multiplier", minval=0.1, step=0.1, group = "ATR TP/SL Settings", tooltip="SL distance = ATR × this multiplier")
show_tp2 = input.bool(false, "Show Take Profit 2", group = "ATR TP/SL Settings")
tp2_multiplier = input.float(3.0, "Take Profit 2 Multiplier", minval=0.1, step=0.1, group = "ATR TP/SL Settings", tooltip="TP2 distance = ATR × this multiplier")
line_length = input.int(20, "TP/SL Line Length", minval=5, maxval=200, group = "ATR TP/SL Settings", tooltip="Length of TP/SL lines in bars - higher values extend lines further to the right")

// Label Text Size Settings
label_text_size = input.string("Medium", "TP/SL Label Text Size", options=["Small", "Medium", "Large"], group = "ATR TP/SL Settings", tooltip="Choose the text size for TP/SL labels for better readability")

// ATR Display Settings
show_atr_info = input.bool(true, "Show ATR Info in Table", group = "ATR TP/SL Settings", tooltip="Display current ATR value and calculated distances in the info table")

show_info_table = input.bool(true, "Show Volume Info Table", group = "Display Settings")
table_position = input.string("Top Right", "Table Position", options=["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group = "Display Settings")
table_size = input.string("Normal", "Table Size", options=["Small", "Normal", "Large"], group = "Display Settings")
table_x_offset = input.int(0, "Table X Offset", minval=-50, maxval=50, group = "Display Settings")
table_y_offset = input.int(0, "Table Y Offset", minval=-50, maxval=50, group = "Display Settings")

shw_vol = input.bool(true, "Show Volume Analysis", "When enabled, displays volume analysis as candle-like bars within the channel. This helps identify volume patterns that may precede breakouts.", group = "Volume Analysis")
show_volume_gauge = input.bool(false, "Show Volume Gauge", "When enabled, displays the volume delta gauge on the right side of active channels.", group = "Volume Analysis")
vol_mode = input.string("Comparison", "Volume Display Mode", options=["Volume", "Comparison", "Delta"], group="Volume Analysis", tooltip="Volume: Shows total volume as symmetrical bars. Comparison: Shows up volume above midline, down volume below. Delta: Shows net volume delta (positive above, negative below midline).")
tf = input.timeframe("1", "Volume Delta Timeframe Source", group = "Volume Analysis", tooltip="The timeframe used to calculate volume delta data. Lower timeframes provide more granular volume analysis but may be noisier.")
vol_scale = input.float(0.5, "Volume Scale", minval=0.1, maxval=2.0, step=0.1, group="Volume Analysis", tooltip="Adjusts the height of volume bars relative to channel size. Higher values make volume bars more prominent, lower values make them more subtle.")

text_size    = input.string("Tiny", "Volume Text Size", options=["Tiny","Small","Medium","Large"], group="Appearance", tooltip="Size of the volume text at the corner of the channels.")
green = input.color(#00ffbb, title = "Bullish Colour", group = "Appearance", tooltip = "Primary colour for bullish visual elements. Adjust for preferred palette - affects bars, fills, and labels when momentum is positive.")
red   = input.color(#ff1100, title = "Bearish Colour", group = "Appearance", tooltip = "Primary colour for bearish visual elements. Adjust for preferred palette - affects bars, fills, and labels when momentum is negative.")
tp_color = input.color(color.green, title = "Take Profit 1 Color", group = "Appearance")
tp2_color = input.color(color.lime, title = "Take Profit 2 Color", group = "Appearance")
sl_color = input.color(color.red, title = "Stop Loss Color", group = "Appearance")

// ============================================================================
// VARIABLES
// ============================================================================

var boxes = array.new_box()
var boxes_u = array.new_box()
var boxes_l = array.new_box()

var line[] gaugeLines = array.new<line>()
var label gaugeLabel = na
var line[] centerLines = array.new<line>()

// ATR-based TP/SL variables
var line tpLine = na
var line tp2Line = na
var line slLine = na
var line entryLine = na
var label entryLabel = na
var label tpLabel = na
var label tp2Label = na
var label slLabel = na

// Trade state tracking
var string currentTradeState = "No Trade"
var string lastBreakoutDirection = na
var float lastBreakoutVolume = na
var bool volumeConfirmsBreakout = false

// TP/SL Alert tracking variables
var float activeTP1Price = na
var float activeTP2Price = na
var float activeSLPrice = na
var string activeTradeDirection = na
var bool tp1Hit = false
var bool tp2Hit = false
var bool slHit = false

// ATR tracking variables
var float currentATR = na
var float calculatedTP1Distance = na
var float calculatedTP2Distance = na
var float calculatedSLDistance = na

// ============================================================================
// FUNCTIONS
// ============================================================================

textSize(text_size) =>
    switch text_size
        "Tiny"  => size.tiny
        "Small" => size.small
        "Medium"=> size.normal
        "Large" => size.large

// Function to get label text size based on user selection
getLabelTextSize(label_size) =>
    switch label_size
        "Small" => size.small
        "Medium" => size.normal
        "Large" => size.large
        => size.normal

f_can_create(float tNew, float bNew) =>
    ok = true
    if array.size(boxes) > 0
        for j = 0 to array.size(boxes)-1
            boxObj = array.get(boxes, j)
            if (tNew > box.get_bottom(boxObj)) and (bNew < box.get_top(boxObj))
                ok := false
                break
    ok

getVolumeTransparency(float vol, float smoothed_vol) =>
    if vol_mode == "Volume"
        float vol_ratio = vol / smoothed_vol
        float transparency = math.max(20, math.min(80, 80 - (vol_ratio - 0.5) * 40))
        transparency
    else
        0

getTablePosition(pos) =>
    switch pos
        "Top Left" => position.top_left
        "Top Right" => position.top_right
        "Bottom Left" => position.bottom_left
        "Bottom Right" => position.bottom_right
        => position.top_right

getTableSize(size_str) =>
    switch size_str
        "Small" => size.tiny
        "Normal" => size.small  
        "Large" => size.normal
        => size.small

analyzeVolumeBreakout(float currentVol, float avgVol, string direction) =>
    volRatio = currentVol / avgVol
    confirmed = volRatio > 1.2  // Volume 20% above average confirms breakout
    string analysis = ""
    
    if confirmed
        analysis := direction + " breakout CONFIRMED\nVolume: " + str.tostring(math.round(volRatio, 1)) + "x average"
    else if volRatio > 0.8
        analysis := direction + " breakout WEAK\nLow volume confirmation"
    else
        analysis := direction + " breakout FAILED\nVolume too low"
    
    [confirmed, analysis]

// ============================================================================
// CALCULATIONS
// ============================================================================

// ATR Calculation from selected timeframe
currentATR := request.security(syminfo.tickerid, atr_timeframe, ta.atr(atr_length))

// Calculate TP/SL distances based on ATR
calculatedTP1Distance := currentATR * tp1_multiplier
calculatedTP2Distance := currentATR * tp2_multiplier
calculatedSLDistance := currentATR * sl_multiplier

lowestLow = ta.lowest(low, length_)
highestHigh = ta.highest(high, length_)
normalizedPrice = (close - lowestLow) / (highestHigh - lowestLow)
vol = ta.stdev(normalizedPrice, 14)

upper = (ta.highestbars(vol, length + 1) + length)/length
lower = (ta.lowestbars(vol, length + 1) + length)/length

upbreak = 0.0
downbreak = 0.0

duration = math.max(nz(ta.barssince(ta.crossover(lower,upper))), 1)
h = ta.highest(duration)
l = ta.lowest(duration)

[uv, dv, vold] = ta.requestUpAndDownVolume(tf)

var hvold = vold
var lvold = vold

if ta.crossover(lower, upper)
    hvold := vold
    lvold := vold

if vold > hvold
    hvold := vold

if vold < lvold
    lvold := vold

smoothedvol = ta.sma(volume, 20)
vola = ta.atr(length)/2

bool newChannelFormed = false
bool bullishBreakout = false
bool bearishBreakout = false

// Check for TP/SL hits before processing new breakouts
if not na(activeTP1Price) and not na(activeTradeDirection)
    if activeTradeDirection == "LONG"
        if high >= activeTP1Price and not tp1Hit
            tp1Hit := true
        if show_tp2 and not na(activeTP2Price) and high >= activeTP2Price and not tp2Hit
            tp2Hit := true
        if low <= activeSLPrice and not slHit
            slHit := true
    else if activeTradeDirection == "SHORT"
        if low <= activeTP1Price and not tp1Hit
            tp1Hit := true
        if show_tp2 and not na(activeTP2Price) and low <= activeTP2Price and not tp2Hit
            tp2Hit := true
        if high >= activeSLPrice and not slHit
            slHit := true

if ta.crossover(upper, lower) and duration > 10
    if overlap or f_can_create(h, l)
        array.unshift(boxes, box.new(bar_index-duration, h, bar_index, l, bgcolor = color.new(chart.fg_color, 90), border_color = na))
        array.unshift(boxes_u, box.new(bar_index-duration, h, bar_index, h-vola, bgcolor = color.new(red, 70), border_color = na))
        array.unshift(boxes_l, box.new(bar_index-duration, l+vola, bar_index, l, bgcolor = color.new(green, 70), border_color = na))
        
        float centerY = (h + l) / 2
        array.unshift(centerLines, line.new(bar_index-duration, centerY, bar_index, centerY, color = color.new(chart.fg_color, 50), width = 1, style = line.style_dashed))
        newChannelFormed := true

if array.size(boxes) > 0
    for i = 0 to array.size(boxes)-1
        boxObj = array.get(boxes, i)
        boxObjU = array.get(boxes_u, i)
        boxObjL = array.get(boxes_l, i)
        centerLineObj = array.get(centerLines, i)
        
        if ((strong ? math.avg(close, open) : close) > box.get_top(boxObj))
            upbreak := box.get_bottom(boxObj)
            
            // Update trade state
            currentTradeState := "Long Trade Active"
            lastBreakoutDirection := "BULLISH"
            lastBreakoutVolume := volume
            [volumeConfirmsBreakout, volumeAnalysis] = analyzeVolumeBreakout(volume, smoothedvol, "BULLISH")
            
            // Reset TP/SL hit flags for new trade
            tp1Hit := false
            tp2Hit := false
            slHit := false
            
            // Clear old TP/SL lines and labels
            if not na(tpLine)
                line.delete(tpLine)
            if not na(tp2Line)
                line.delete(tp2Line)
            if not na(slLine)
                line.delete(slLine)
            if not na(entryLine)
                line.delete(entryLine)
            if not na(entryLabel)
                label.delete(entryLabel)
            if not na(tpLabel)
                label.delete(tpLabel)
            if not na(tp2Label)
                label.delete(tp2Label)
            if not na(slLabel)
                label.delete(slLabel)
            
            // Create new ATR-based TP/SL lines for bullish breakout
            if show_tpsl and not na(currentATR)
                entry_price = box.get_top(boxObj)
                tp_price = entry_price + calculatedTP1Distance
                tp2_price = entry_price + calculatedTP2Distance
                sl_price = entry_price - calculatedSLDistance
                
                // Set active prices for alert tracking
                activeTP1Price := tp_price
                activeTP2Price := show_tp2 ? tp2_price : na
                activeSLPrice := sl_price
                activeTradeDirection := "LONG"
                
                entryLine := line.new(bar_index, entry_price, bar_index + line_length, entry_price, color=color.white, width=2)
                tpLine := line.new(bar_index, tp_price, bar_index + line_length, tp_price, color=tp_color, width=1)
                slLine := line.new(bar_index, sl_price, bar_index + line_length, sl_price, color=sl_color, width=1)
                
                // Create labels with symbols, no background color and user-selected text size
                entryLabel := label.new(bar_index + line_length, entry_price, "▶ LONG " + str.tostring(entry_price, format.mintick), color=na, textcolor=color.white, size=getLabelTextSize(label_text_size), style=label.style_label_left)
                tpLabel := label.new(bar_index + line_length, tp_price, "🎯 TP1 " + str.tostring(tp_price, format.mintick) + " (+" + str.tostring(calculatedTP1Distance, "#.##") + ")", color=na, textcolor=tp_color, size=getLabelTextSize(label_text_size), style=label.style_label_left)
                slLabel := label.new(bar_index + line_length, sl_price, "🛑 SL " + str.tostring(sl_price, format.mintick) + " (-" + str.tostring(calculatedSLDistance, "#.##") + ")", color=na, textcolor=sl_color, size=getLabelTextSize(label_text_size), style=label.style_label_left)
                
                if show_tp2
                    tp2Line := line.new(bar_index, tp2_price, bar_index + line_length, tp2_price, color=tp2_color, width=1)
                    tp2Label := label.new(bar_index + line_length, tp2_price, "🎯 TP2 " + str.tostring(tp2_price, format.mintick) + " (+" + str.tostring(calculatedTP2Distance, "#.##") + ")", color=na, textcolor=tp2_color, size=getLabelTextSize(label_text_size), style=label.style_label_left)
            
            array.remove(boxes, i)
            array.remove(boxes_u, i)
            array.remove(boxes_l, i)
            array.remove(centerLines, i)
            bullishBreakout := true
            
        else if ((strong ? math.avg(close, open) : close) < box.get_bottom(boxObj))
            downbreak := box.get_top(boxObj)
            
            // Update trade state
            currentTradeState := "Short Trade Active"
            lastBreakoutDirection := "BEARISH"
            lastBreakoutVolume := volume
            [volumeConfirmsBreakout, volumeAnalysis] = analyzeVolumeBreakout(volume, smoothedvol, "BEARISH")
            
            // Reset TP/SL hit flags for new trade
            tp1Hit := false
            tp2Hit := false
            slHit := false
            
            // Clear old TP/SL lines and labels
            if not na(tpLine)
                line.delete(tpLine)
            if not na(tp2Line)
                line.delete(tp2Line)
            if not na(slLine)
                line.delete(slLine)
            if not na(entryLine)
                line.delete(entryLine)
            if not na(entryLabel)
                label.delete(entryLabel)
            if not na(tpLabel)
                label.delete(tpLabel)
            if not na(tp2Label)
                label.delete(tp2Label)
            if not na(slLabel)
                label.delete(slLabel)
            
            // Create new ATR-based TP/SL lines for bearish breakout
            if show_tpsl and not na(currentATR)
                entry_price = box.get_bottom(boxObj)
                tp_price = entry_price - calculatedTP1Distance
                tp2_price = entry_price - calculatedTP2Distance
                sl_price = entry_price + calculatedSLDistance
                
                // Set active prices for alert tracking
                activeTP1Price := tp_price
                activeTP2Price := show_tp2 ? tp2_price : na
                activeSLPrice := sl_price
                activeTradeDirection := "SHORT"
                
                entryLine := line.new(bar_index, entry_price, bar_index + line_length, entry_price, color=color.white, width=2)
                tpLine := line.new(bar_index, tp_price, bar_index + line_length, tp_price, color=tp_color, width=1)
                slLine := line.new(bar_index, sl_price, bar_index + line_length, sl_price, color=sl_color, width=1)
                
                // Create labels with symbols, no background color and user-selected text size
                entryLabel := label.new(bar_index + line_length, entry_price, "◀ SHORT " + str.tostring(entry_price, format.mintick), color=na, textcolor=color.white, size=getLabelTextSize(label_text_size), style=label.style_label_left)
                tpLabel := label.new(bar_index + line_length, tp_price, "🎯 TP1 " + str.tostring(tp_price, format.mintick) + " (-" + str.tostring(calculatedTP1Distance, "#.##") + ")", color=na, textcolor=tp_color, size=getLabelTextSize(label_text_size), style=label.style_label_left)
                slLabel := label.new(bar_index + line_length, sl_price, "🛑 SL " + str.tostring(sl_price, format.mintick) + " (+" + str.tostring(calculatedSLDistance, "#.##") + ")", color=na, textcolor=sl_color, size=getLabelTextSize(label_text_size), style=label.style_label_left)
                
                if show_tp2
                    tp2Line := line.new(bar_index, tp2_price, bar_index + line_length, tp2_price, color=tp2_color, width=1)
                    tp2Label := label.new(bar_index + line_length, tp2_price, "🎯 TP2 " + str.tostring(tp2_price, format.mintick) + " (-" + str.tostring(calculatedTP2Distance, "#.##") + ")", color=na, textcolor=tp2_color, size=getLabelTextSize(label_text_size), style=label.style_label_left)
            
            array.remove(boxes, i)
            array.remove(boxes_u, i)
            array.remove(boxes_l, i)
            array.remove(centerLines, i)
            bearishBreakout := true
                  
        else
            box.set_right(boxObj, bar_index)
            box.set_right(boxObjU, bar_index)
            box.set_right(boxObjL, bar_index)
            line.set_x2(centerLineObj, bar_index)
            
            // Add volume text to appropriate box based on price position
            float boxMidline = (box.get_top(boxObj) + box.get_bottom(boxObj)) / 2
            float currentPrice = strong ? math.avg(close, open) : close
            
            string volText = ""
            if vol_mode == "Volume"
                volText := str.tostring(math.round(volume / 1000, 1)) + "K"
            else if vol_mode == "Comparison"
                volText := str.tostring(math.round(uv / 1000, 1)) + "K/" + str.tostring(math.round(dv / 1000, 1)) + "K"
            else if vol_mode == "Delta"
                volText := str.tostring(math.round(vold / 1000, 1)) + "K"
            
            if currentPrice > boxMidline
                box.set_text(boxObjL, volText)
                box.set_text_halign(boxObjL, text.align_right)
                box.set_text_color(boxObjL, color.new(chart.fg_color, 30))
                box.set_text_size(boxObjL, textSize(text_size))
                box.set_text(boxObjU, "")
            else
                box.set_text(boxObjU, volText)
                box.set_text_halign(boxObjU, text.align_right)
                box.set_text_color(boxObjU, color.new(chart.fg_color, 30))
                box.set_text_size(boxObjU, textSize(text_size))
                box.set_text(boxObjL, "")

float currentMidline = na
float channelHeight = na

if array.size(boxes) > 0
    boxObj = array.get(boxes, 0)
    float topBound = box.get_top(boxObj)
    float bottomBound = box.get_bottom(boxObj)
    currentMidline := (topBound + bottomBound) / 2
    channelHeight := (topBound - bottomBound) * vol_scale

float vol_upper_open = na
float vol_upper_high = na  
float vol_upper_low = na
float vol_upper_close = na

float vol_lower_open = na
float vol_lower_high = na
float vol_lower_low = na  
float vol_lower_close = na

if not na(currentMidline) and not na(channelHeight) and shw_vol
    if vol_mode == "Volume"
        float vol_height = (volume / smoothedvol) * (channelHeight / 4)
        vol_upper_open := currentMidline
        vol_upper_close := currentMidline + vol_height
        vol_upper_high := currentMidline + vol_height
        vol_upper_low := currentMidline
        
        vol_lower_open := currentMidline
        vol_lower_close := currentMidline - vol_height
        vol_lower_high := currentMidline
        vol_lower_low := currentMidline - vol_height
        
    else if vol_mode == "Comparison"
        float uv_height = na(uv) ? 0 : (uv / smoothedvol) * (channelHeight / 4)
        float dv_height = na(dv) ? 0 : (dv / smoothedvol) * (channelHeight / 4)
        
        vol_upper_open := currentMidline
        vol_upper_close := currentMidline + uv_height  
        vol_upper_high := currentMidline + uv_height
        vol_upper_low := currentMidline
        
        vol_lower_open := currentMidline
        vol_lower_close := currentMidline + dv_height
        vol_lower_high := currentMidline  
        vol_lower_low := currentMidline + dv_height
        
    else if vol_mode == "Delta" 
        float delta_height = na(vold) ? 0 : math.abs(vold / smoothedvol) * (channelHeight / 4)
        
        if vold >= 0
            vol_upper_open := currentMidline
            vol_upper_close := currentMidline + delta_height
            vol_upper_high := currentMidline + delta_height  
            vol_upper_low := currentMidline
            
            vol_lower_open := currentMidline
            vol_lower_close := currentMidline
            vol_lower_high := currentMidline
            vol_lower_low := currentMidline
        else
            vol_upper_open := currentMidline
            vol_upper_close := currentMidline
            vol_upper_high := currentMidline
            vol_upper_low := currentMidline
            
            vol_lower_open := currentMidline
            vol_lower_close := currentMidline - delta_height  
            vol_lower_high := currentMidline
            vol_lower_low := currentMidline - delta_height

color upperColor = na
color lowerColor = na

if vol_mode == "Volume"
    float transparency = getVolumeTransparency(volume, smoothedvol)
    upperColor := color.new(chart.fg_color, transparency)
    lowerColor := color.new(chart.fg_color, transparency)
else
    upperColor := green
    lowerColor := red

// ============================================================================
// VISUALS
// ============================================================================

plotshape(upbreak != 0 ? upbreak : na, "Bullish Breakout Signal", shape.labelup, location.absolute, green, text = "▲", textcolor = chart.fg_color)
plotshape(downbreak != 0 ? downbreak : na, "Bearish Breakout Signal", shape.labeldown, location.absolute, red, text = "▼", textcolor = chart.fg_color)

plotcandle(vol_upper_open, vol_upper_high, vol_upper_low, vol_upper_close, color=upperColor, wickcolor=upperColor, bordercolor=upperColor)
plotcandle(vol_lower_open, vol_lower_high, vol_lower_low, vol_lower_close, color=lowerColor, wickcolor=lowerColor, bordercolor=lowerColor)

volumeAvailable = not na(volume)

if barstate.islast
    bool channelActive = array.size(boxes) > 0 and upbreak == 0 and downbreak == 0
    
    if array.size(gaugeLines) > 0
        for ln in gaugeLines
            line.delete(ln)
        array.clear(gaugeLines)
    
    if not na(gaugeLabel)
        label.delete(gaugeLabel)
        gaugeLabel := na
    
    if channelActive and show_volume_gauge
        boxObj = array.get(boxes, 0)
        float topBound = box.get_top(boxObj)
        float bottomBound = box.get_bottom(boxObj)
        
        if not na(topBound) and not na(bottomBound) and topBound != bottomBound
            int segments = 21
            float segLen = (topBound - bottomBound) / segments
            
            for i = 0 to segments - 1
                float y1 = topBound   - i * segLen
                float y2 = topBound   - (i + 1) * segLen
                color segCol = color.from_gradient(y1, bottomBound, topBound, red, green)
                line ln = line.new(x1 = bar_index + 2,
                                     y1 = y1,
                                     x2 = bar_index + 2,
                                     y2 = y2,
                                     color = segCol,
                                     width = 4)
                array.unshift(gaugeLines, ln)
            
            float delvol = -100*2*((vold-lvold)/(hvold-lvold)-0.5)
            delvol := math.max(math.min(delvol, 100), -100)
            float pointerPos = topBound - ((delvol + 100) / 200) * (topBound - bottomBound)
            gaugeLabel := label.new(bar_index + 3, pointerPos, "◀", color = na, textcolor = chart.fg_color, size = size.small, style = label.style_label_left)

if not volumeAvailable
    var gaugeWarnTable = table.new(position = position.top_right, columns = 1, rows = 1, bgcolor = red, border_width = 1, border_color = chart.fg_color, frame_color = chart.fg_color, frame_width = 1)
    table.cell(gaugeWarnTable, 0, 0, "Volume not available\nGauge may not work as expected", text_color = chart.fg_color, text_halign = text.align_center, text_size = size.small)

// Enhanced Volume Information Table with ATR Info
if show_info_table
    var infoTable = table.new(position = getTablePosition(table_position), columns = 2, rows = show_atr_info ? 12 : 8, bgcolor = color.new(chart.bg_color, 10), border_width = 1, border_color = chart.fg_color, frame_color = chart.fg_color, frame_width = 1)
    
    // Clear existing table content
    if show_atr_info
        table.clear(infoTable, 0, 0, 1, 11)
    else
        table.clear(infoTable, 0, 0, 1, 7)
    
    textSize = getTableSize(table_size)
    
    // Header
    table.cell(infoTable, 0, 0, "BOCS ADAPTIVE ANALYSIS", text_color = chart.fg_color, text_halign = text.align_center, text_size = textSize, bgcolor = color.new(chart.fg_color, 80))
    table.cell(infoTable, 1, 0, "", text_color = chart.fg_color, text_halign = text.align_center, text_size = textSize, bgcolor = color.new(chart.fg_color, 80))
    table.merge_cells(infoTable, 0, 0, 1, 0)
    
    int currentRow = 1
    
    // Trade Status
    tradeStatusColor = currentTradeState == "No Trade" ? color.gray : (str.contains(currentTradeState, "Long") ? green : red)
    table.cell(infoTable, 0, currentRow, "Trade Status:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
    table.cell(infoTable, 1, currentRow, currentTradeState, text_color = tradeStatusColor, text_halign = text.align_right, text_size = textSize)
    currentRow += 1
    
    // Channel Status
    channelStatus = array.size(boxes) > 0 ? "Active Channel" : "No Channel"
    channelColor = array.size(boxes) > 0 ? color.yellow : color.gray
    table.cell(infoTable, 0, currentRow, "Channel Status:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
    table.cell(infoTable, 1, currentRow, channelStatus, text_color = channelColor, text_halign = text.align_right, text_size = textSize)
    currentRow += 1
    
    // ATR Information Section
    if show_atr_info
        // ATR Value
        string atrValue = na(currentATR) ? "N/A" : str.tostring(currentATR, "#.##")
        table.cell(infoTable, 0, currentRow, "ATR (" + atr_timeframe + "):", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, atrValue, text_color = color.orange, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
        
        // TP1 Distance
        string tp1Distance = na(calculatedTP1Distance) ? "N/A" : str.tostring(calculatedTP1Distance, "#.##") + " pts"
        table.cell(infoTable, 0, currentRow, "TP1 Distance:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, tp1Distance + " (" + str.tostring(tp1_multiplier, "#.#") + "x)", text_color = tp_color, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
        
        // TP2 Distance (if enabled)
        if show_tp2
            string tp2Distance = na(calculatedTP2Distance) ? "N/A" : str.tostring(calculatedTP2Distance, "#.##") + " pts"
            table.cell(infoTable, 0, currentRow, "TP2 Distance:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
            table.cell(infoTable, 1, currentRow, tp2Distance + " (" + str.tostring(tp2_multiplier, "#.#") + "x)", text_color = tp2_color, text_halign = text.align_right, text_size = textSize)
            currentRow += 1
        
        // SL Distance
        string slDistance = na(calculatedSLDistance) ? "N/A" : str.tostring(calculatedSLDistance, "#.##") + " pts"
        table.cell(infoTable, 0, currentRow, "SL Distance:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, slDistance + " (" + str.tostring(sl_multiplier, "#.#") + "x)", text_color = sl_color, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
    
    // Last Breakout Analysis
    if not na(lastBreakoutDirection)
        [confirmed, analysis] = analyzeVolumeBreakout(lastBreakoutVolume, smoothedvol, lastBreakoutDirection)
        breakoutColor = confirmed ? green : red
        
        table.cell(infoTable, 0, currentRow, "Last Breakout:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, lastBreakoutDirection, text_color = breakoutColor, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
        
        table.cell(infoTable, 0, currentRow, "Volume Confirm:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        confirmText = confirmed ? "CONFIRMED" : "WEAK"
        table.cell(infoTable, 1, currentRow, confirmText, text_color = breakoutColor, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
    else
        table.cell(infoTable, 0, currentRow, "Last Breakout:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, "None", text_color = color.gray, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
        
        table.cell(infoTable, 0, currentRow, "Volume Confirm:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, "N/A", text_color = color.gray, text_halign = text.align_right, text_size = textSize)
        currentRow += 1
    
    // Current Volume Analysis
    string currentVol = na(volume) ? "N/A" : str.tostring(math.round(volume / 1000, 1)) + "K"
    string avgVol = na(smoothedvol) ? "N/A" : str.tostring(math.round(smoothedvol / 1000, 1)) + "K"
    
    table.cell(infoTable, 0, currentRow, "Current Vol:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
    table.cell(infoTable, 1, currentRow, currentVol, text_color = chart.fg_color, text_halign = text.align_right, text_size = textSize)
    currentRow += 1
    
    table.cell(infoTable, 0, currentRow, "Average Vol:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
    table.cell(infoTable, 1, currentRow, avgVol, text_color = chart.fg_color, text_halign = text.align_right, text_size = textSize)
    currentRow += 1
    
    // Volume Ratio Analysis
    if not na(volume) and not na(smoothedvol)
        volRatio = volume / smoothedvol
        string volStatus = volRatio > 1.5 ? "Very High" : volRatio > 1.2 ? "High" : volRatio > 0.8 ? "Normal" : "Low"
        color volColor = volRatio > 1.2 ? green : volRatio < 0.8 ? red : color.yellow
        
        table.cell(infoTable, 0, currentRow, "Vol Status:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, volStatus + " (" + str.tostring(math.round(volRatio, 1)) + "x)", text_color = volColor, text_halign = text.align_right, text_size = textSize)
    else
        table.cell(infoTable, 0, currentRow, "Vol Status:", text_color = chart.fg_color, text_halign = text.align_left, text_size = textSize)
        table.cell(infoTable, 1, currentRow, "N/A", text_color = color.gray, text_halign = text.align_right, text_size = textSize)

// ============================================================================
// ALERTS
// ============================================================================

alertcondition(newChannelFormed, "New Channel Formation", "A new breakout channel has been formed")
alertcondition(bullishBreakout, "Bullish Breakout", "Price has broken out above the channel (bullish signal)")
alertcondition(bearishBreakout, "Bearish Breakout", "Price has broken out below the channel (bearish signal)")

// TP/SL Alert Conditions
alertcondition(tp1Hit, "Take Profit 1 Hit", "Take Profit 1 level has been reached")
alertcondition(tp2Hit and show_tp2, "Take Profit 2 Hit", "Take Profit 2 level has been reached")
alertcondition(slHit, "Stop Loss Hit", "Stop Loss level has been reached")

// ============================================================================
// OUTPUTS
// ============================================================================

plot(bullishBreakout ? 1 : 0, "Bullish Signal", color.new(green, 100), display=display.data_window)
plot(bearishBreakout ? 1 : 0, "Bearish Signal", color.new(red, 100), display=display.data_window)
plot(newChannelFormed ? 1 : 0, "New Channel", color.new(color.blue, 100), display=display.data_window)
plot(tp1Hit ? 1 : 0, "TP1 Hit", color.new(tp_color, 100), display=display.data_window)
plot(tp2Hit ? 1 : 0, "TP2 Hit", color.new(tp2_color, 100), display=display.data_window)
plot(slHit ? 1 : 0, "SL Hit", color.new(sl_color, 100), display=display.data_window)

// ATR and calculated distances for data window
plot(currentATR, "ATR Value", color.new(color.orange, 100), display=display.data_window)
plot(calculatedTP1Distance, "TP1 Distance", color.new(tp_color, 100), display=display.data_window)
plot(calculatedTP2Distance, "TP2 Distance", color.new(tp2_color, 100), display=display.data_window)
plot(calculatedSLDistance, "SL Distance", color.new(sl_color, 100), display=display.data_window)